<!DOCTYPE HTML>
<html lang="en">
<head>
  <title>three.js - geometry - marching cubes - webgl</title>
  <meta charset="utf-8">
  <style type="text/css">
    body {
      color: #fff;
      font-family: Monospace;
      font-size: 13px;
      text-align: center;

      background-color: #000;
      margin: 0px;
      overflow: hidden;
    }

    #info {
      color: #ffffff;
      position: absolute;
      top: 0px;
      width: 100%;
      padding: 5px;
    }

    a {
      color: gold;
    }

    #oldie {
      font-family: monospace;
      font-size: 13px;

      text-align: center;
      background: rgb(0, 0, 50);
      color: #fff;
      padding: 1em;

      width: 475px;
      margin: 5em auto 0;

      display: none;
    }

  </style>
</head>
<body>

<div id="container"></div>
<div id="info">
  <a href="hxxp://github.com/mrdoob/three.js" target="_blank">three.js</a> -
  marching cubes - webgl
  [based on greggman's <a
    href="hxxp://code.google.com/p/webglsamples/blob/blob.html">blob</a>]
</div>

<center>
  <div id="oldie">
    Sorry, your browser doesn't support <a
      href="hxxp://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">WebGL</a>.<br/>
    <br/>
    Please try in
    <a href="hxxp://www.chromium.org/getting-involved/dev-channel">Chrome 9+</a>
    /
    <a href="hxxp://www.mozilla.com/en-US/firefox/all-beta.html">Firefox 4+</a>
    /
    <a href="hxxp://nightly.webkit.org/">Safari OSX 10.6+</a>
  </div>
</center>

<script type="text/javascript" src="ThreeExtras.js"></script>
<script type="text/javascript" src="../shared/ShaderExtras.js"></script>
<script type="text/javascript" src="../shared/Stats.js"></script>
<script type="text/javascript" src="../shared/gui.min.js"></script>


<script type="text/javascript" id="main-code">

if (!is_browser_compatible()) {

  document.getElementById("oldie").style.display = "block";

}


var container, stats;

var camera, scene, renderer;

var mesh, texture, geometry, materials, material, current_material;

var materialFilm, rtTexture1, cameraOrtho, scenePost, quad;

var light, pointLight;

var effect, resolution, numBlobs;

var worldWidth = 128, worldDepth = 128,
    worldHalfWidth = worldWidth / 2, worldHalfDepth = worldDepth / 2;

var mouseX = 0, mouseY = 0,
    lat = 0, lon = 0, phy = 0, theta = 0;

var direction = new THREE.Vector3(),
    moveForward = false, moveBackward = false, moveLeft = false, moveRight = false;

var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;

var effectController;

init();
setInterval(loop, 1000 / 60);
//loop();

function init() {

  container = document.getElementById('container');

  camera = new THREE.Camera(50, window.innerWidth / window.innerHeight, 1, 10000);
  camera.position.set(0, 0, 2000);

  scene = new THREE.Scene();

  light = new THREE.DirectionalLight(0xffffff);
  light.position.x = 0.5;
  light.position.y = 0.5;
  light.position.z = 1;
  light.position.normalize();
  scene.addLight(light);

  pointLight = new THREE.PointLight(0xff3300);
  pointLight.position.x = 0;
  pointLight.position.y = 0;
  pointLight.position.z = 100;
  scene.addLight(pointLight);

  /*
   var lightMesh = new THREE.Mesh( new Sphere( 5, 16, 8 ), new THREE.MeshBasicMaterial( { color: 0xff0000 } ) );
   lightMesh.position = pointLight.position;
   scene.addObject( lightMesh );
   */

  // toon shaders

  function createShaderMaterial(id, light) {

    var smaterial, shader, vs, fs, u;

    shader = ShaderExtras[ id ];
    u = Uniforms.clone(shader.uniforms);
    vs = shader.vertex_shader;
    fs = shader.fragment_shader;

    smaterial = new THREE.MeshShaderMaterial({ uniforms: u, vertex_shader: vs,  fragment_shader: fs });

    smaterial.uniforms.uDirLightPos.value = light.position;
    smaterial.uniforms.uDirLightColor.value = light.color;

    return smaterial;

  }

  var toonMaterial5 = createShaderMaterial("toon5", light),
      toonMaterial3 = createShaderMaterial("toon3", light),
      hatchingMaterial = createShaderMaterial("hatching", light),
      hatchingMaterial2 = createShaderMaterial("hatching", light),
      dottedMaterial = createShaderMaterial("dotted", light),
      dottedMaterial2 = createShaderMaterial("dotted", light);

  hatchingMaterial2.uniforms.uBaseColor.value.setRGB(0, 0, 0);
  hatchingMaterial2.uniforms.uLineColor1.value.setHSV(0, 0.9, 0.9);
  hatchingMaterial2.uniforms.uLineColor2.value.setHSV(0, 0.9, 0.9);
  hatchingMaterial2.uniforms.uLineColor3.value.setHSV(0, 0.9, 0.9);
  hatchingMaterial2.uniforms.uLineColor4.value.setHSV(0.1, 0.9, 0.9);

  dottedMaterial2.uniforms.uBaseColor.value.setRGB(0, 0, 0);
  dottedMaterial2.uniforms.uLineColor1.value.setHSV(0.05, 1.0, 1.0);

  // environment map

  var path = "../shared/textures/cube/SwedishRoyalCastle/";
  var format = '.jpg';
  var urls = [
    path + 'px' + format, path + 'nx' + format,
    path + 'py' + format, path + 'ny' + format,
    path + 'pz' + format, path + 'nz' + format
  ];

  var images = ImageUtils.loadArray(urls);

  var reflectionCube = new THREE.Texture(images);
  var refractionCube = new THREE.Texture(images, new THREE.CubeRefractionMapping());

  materials = {

    "chrome" :
    {
      m: new THREE.MeshLambertMaterial({ color: 0xffffff, env_map: reflectionCube }),
      h: 0, s: 0, v: 1
    },

    "liquid" :
    {
      m: new THREE.MeshLambertMaterial({ color: 0xffffff, env_map: refractionCube, refraction_ratio: 0.85 }),
      h: 0, s: 0, v: 1
    },

    "shiny"  :
    {
      m: new THREE.MeshPhongMaterial({ color: 0x550000, specular:0x220000, env_map: reflectionCube, combine: THREE.MixOperation, reflectivity: 0.3 }),
      h: 0, s: 0.9, v: 0.3
    },

    "matte" :
    {
      m: new THREE.MeshLambertMaterial({ color: 0xffffff }),
      h: 0, s: 0, v: 1
    },

    "toon5"  :
    {
      m: toonMaterial5,
      h: 0.2, s: 0.5, v: 1
    },

    "toon3" :
    {
      m: toonMaterial3,
      h: 0.4, s: 0.5, v: 1
    },

    "hatching" :
    {
      m: hatchingMaterial,
      h: 0.2, s: 0.2, v: 1
    },

    "hatching2" :
    {
      m: hatchingMaterial2,
      h: 0.0, s: 0.9, v: 0.9
    },

    "dotted" :
    {
      m: dottedMaterial,
      h: 0.2, s: 0.2, v: 1
    },

    "dotted2" :
    {
      m: dottedMaterial2,
      h: 0.1, s: 1.0, v: 1
    }

  };


  material = new THREE.MeshLambertMaterial({ ambient: 0x030303, color: 0xdddddd, specular: 0xffaa00, shininess: 10 });

  resolution = 28;
  numBlobs = 10;

  current_material = "shiny";

  effect = new THREE.MarchingCubes(resolution, materials[ current_material ].m);
  effect.position.x = 0;
  effect.position.y = 0;
  effect.position.z = 0;
  effect.scale.x = effect.scale.y = effect.scale.z = 700;
  effect.updateMatrix();
  scene.addObject(effect);

  // postprocessing

  scenePost = new THREE.Scene();

  cameraOrtho = new THREE.Camera();
  cameraOrtho.projectionMatrix = THREE.Matrix4.makeOrtho(window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, -10000, 10000);
  cameraOrtho.position.z = 100;

  rtTexture1 = new THREE.RenderTarget(window.innerWidth, window.innerHeight, { min_filter: THREE.LinearFilter, mag_filter: THREE.NearestFilter });

  var film_shader = ShaderUtils.lib["film"];
  var film_uniforms = Uniforms.clone(film_shader.uniforms);

  film_uniforms["tDiffuse"].texture = rtTexture1;

  materialFilm = new THREE.MeshShaderMaterial({

        uniforms: film_uniforms,
        vertex_shader: film_shader.vertex_shader,
        fragment_shader: film_shader.fragment_shader

      });

  materialFilm.uniforms.grayscale.value = 0;

  var plane = new Plane(window.innerWidth, window.innerHeight);
  quad = new THREE.Mesh(plane, materialFilm);
  quad.position.z = -500;
  scenePost.addObject(quad);

  renderer = new THREE.WebGLRenderer({ clearColor: 0x000000, clearAlpha: 1 });
  renderer.setSize(window.innerWidth, window.innerHeight);

  container.innerHTML = "";

  container.appendChild(renderer.domElement);

  stats = new Stats();
  stats.domElement.style.position = 'absolute';
  stats.domElement.style.top = '0px';
  container.appendChild(stats.domElement);

  document.addEventListener('mousedown', onDocumentMouseDown, false);
  document.addEventListener('mouseup', onDocumentMouseUp, false);
  document.addEventListener('mousemove', onDocumentMouseMove, false);
  document.addEventListener('contextmenu', function (event) {
    event.preventDefault();
  }, false);

  document.addEventListener('keydown', onDocumentKeyDown, false);
  document.addEventListener('keyup', onDocumentKeyUp, false);

  setupGui();

}

function setupGui() {

  var createHandler = function(id) {

    return function() {

      var mat_old = materials[ current_material ];
      mat_old.h = m_h.getValue();
      mat_old.s = m_s.getValue();
      mat_old.v = m_v.getValue();

      current_material = id;

      var mat = materials[ id ];
      effect.materials = [ mat.m ];

      m_h.setValue(mat.h);
      m_s.setValue(mat.s);
      m_v.setValue(mat.v);

      mm.setValue(current_material);

    };

  };

  function toggle(e) {

    if (e.style.display == "block")
      e.style.display = "none";
    else
      e.style.display = "block";

  }

  effectController = {

    material:   "shiny",

    speed :   1.0,
    numBlobs:   10,
    resolution: 28,
    isolation:  80,

    floor: true,
    wallx: false,
    wallz: false,

    hue:    0.0,
    saturation: 0.9,
    value:    0.3,

    lhue:     0.04,
    lsaturation: 1.0,
    lvalue:     1.0,

    lx: 0.5,
    ly: 0.5,
    lz: 1.0,

    postprocessing: false,

    h_m:  function() {
      for (var i = 0; i < g_m.length; i++)  toggle(g_m[ i ].domElement);
    },
    h_c:  function() {
      for (var i = 0; i < g_c.length; i++)  toggle(g_c[ i ].domElement);
    },
    h_pc: function() {
      for (var i = 0; i < g_pc.length; i++) toggle(g_pc[ i ].domElement);
    },
    h_do: function() {
      for (var i = 0; i < g_do.length; i++) toggle(g_do[ i ].domElement);
    },
    h_s:  function() {
      for (var i = 0; i < g_s.length; i++)  toggle(g_s[ i ].domElement);
    },
    h_r:  function() {
      for (var i = 0; i < g_r.length; i++)  toggle(g_r[ i ].domElement);
    },

    dummy: function() {
    }

  };

  var e1, e2, e3, e4, e5, e6, h, m_h, m_s, m_v,
      g_m = [], g_c, g_pc, g_do, g_s, g_r, mm,
      gui = new GUI();

  // material (type)

  mm = gui.add(effectController, "material");
  mm.domElement.style.display = "none";

  h = gui.add(effectController, "h_m").name("Materials");
  setGuiHeaderStyle(h, 0, 65, 50);

  for (var m in materials) {

    effectController[ m ] = createHandler(m);

    e1 = gui.add(effectController, m).name(m);
    setGuiElementStyle([ e1 ], 0, 65, 50, "block");
    g_m.push(e1);

  }

  // material (color)

  h = gui.add(effectController, "h_c").name("Material color");

  m_h = gui.add(effectController, "hue", 0.0, 1.0, 0.025);
  m_s = gui.add(effectController, "saturation", 0.0, 1.0, 0.025);
  m_v = gui.add(effectController, "value", 0.0, 1.0, 0.025);

  g_c = [ m_h, m_s, m_v ];

  setGuiHeaderStyle(h, 20, 65, 50);
  setGuiElementStyle(g_c, 20, 65, 50, "block");

  // light (point)

  h = gui.add(effectController, "h_pc").name("Point light color");

  e1 = gui.add(effectController, "lhue", 0.0, 1.0, 0.025).name("hue");
  e2 = gui.add(effectController, "lsaturation", 0.0, 1.0, 0.025).name("saturation");
  e3 = gui.add(effectController, "lvalue", 0.0, 1.0, 0.025).name("value");

  g_pc = [ e1, e2, e3 ];

  setGuiHeaderStyle(h, 50, 65, 50);
  setGuiElementStyle(g_pc, 50, 65, 50);

  // light (directional)

  h = gui.add(effectController, "h_do").name("Directional light orientation");

  e1 = gui.add(effectController, "lx", -1.0, 1.0, 0.025).name("x");
  e2 = gui.add(effectController, "ly", -1.0, 1.0, 0.025).name("y");
  e3 = gui.add(effectController, "lz", -1.0, 1.0, 0.025).name("z");

  g_do = [ e1, e2, e3 ];

  setGuiHeaderStyle(h, 80, 65, 50);
  setGuiElementStyle(g_do, 80, 65, 50);

  // simulation

  h = gui.add(effectController, "h_s").name("Simulation");

  e1 = gui.add(effectController, "speed", 0.1, 8.0, 0.05);
  e2 = gui.add(effectController, "numBlobs", 1, 50, 1);
  e3 = gui.add(effectController, "resolution", 14, 40, 1);
  e4 = gui.add(effectController, "isolation", 10, 300, 1);

  e5 = gui.add(effectController, "floor");
  e6 = gui.add(effectController, "wallx");
  e7 = gui.add(effectController, "wallz");

  e5.updateDisplay();
  e6.updateDisplay();
  e7.updateDisplay();

  g_s = [ e1, e2, e3, e4, e5, e6, e7 ];

  setGuiHeaderStyle(h, 200, 65, 50);
  setGuiElementStyle(g_s, 200, 65, 50, "block");

  // rendering

  h = gui.add(effectController, "h_r").name("Rendering");
  e1 = gui.add(effectController, "postprocessing");

  g_r = [ e1 ];

  setGuiHeaderStyle(h, 225, 65, 50);
  setGuiElementStyle(g_r, 225, 65, 50, "block");

  // save

  e1 = gui.add(GUI, "saveURL").name("Save to URL");
  setGuiHeaderStyle(e1, 250, 65, 50, "block");

  gui.domElement.style.backgroundColor = "#222";

  // restore material from URL

  id = mm.getValue()
  current_material = id;
  var mat = materials[ id ];
  effect.materials = [ mat.m ];

}


function setGuiHeaderStyle(g, h, s, v) {

  var color = "hsl(" + h + "," + s + "%, " + v + "%)";

  g.domElement.style.borderLeft = "solid 5px " + color;
  g.domElement.style.background = color;
  g.domElement.style.fontWeight = "bold";

}

function setGuiElementStyle(a, h, s, v, display) {

  var s, color = "hsl(" + h + "," + s + "%, " + v + "%)";

  for (i = 0; i < a.length; i++) {

    s = a[ i ].domElement.style;
    s.borderLeft = "solid 5px " + color;
    s.display = display ? display : "none";

  }

}

function onDocumentMouseDown(event) {

  event.preventDefault();
  event.stopPropagation();

  switch (event.button) {

    case 0: moveForward = true; break;
    case 2: moveBackward = true; break;

  }

}

function onDocumentMouseUp(event) {

  event.preventDefault();
  event.stopPropagation();

  switch (event.button) {

    case 0: moveForward = false; break;
    case 2: moveBackward = false; break;

  }

}

function onDocumentMouseMove(event) {

  mouseX = event.clientX - windowHalfX;
  mouseY = event.clientY - windowHalfY;

}

function onDocumentKeyDown(event) {

  switch (event.keyCode) {

    case 38: /*up*/
    case 87: /*W*/ moveForward = true; break;

    case 37: /*left*/
    case 65: /*A*/ moveLeft = true; break;

    case 40: /*down*/
    case 83: /*S*/ moveBackward = true; break;

    case 39: /*right*/
    case 68: /*D*/ moveRight = true; break;
  }

}

function onDocumentKeyUp(event) {

  switch (event.keyCode) {

    case 38: /*up*/
    case 87: /*W*/ moveForward = false; break;

    case 37: /*left*/
    case 65: /*A*/ moveLeft = false; break;

    case 40: /*down*/
    case 83: /*S*/ moveBackward = false; break;

    case 39: /*right*/
    case 68: /*D*/ moveRight = false; break;
  }

}

var u, delta, time = 0, last = new Date().getTime();

function loop() {

  current = new Date().getTime();
  delta = current - last;
  last = current;
  time += delta * effectController.speed * 0.0002;


  /*
   if ( moveForward )  camera.translateZ( - 10 );
   if ( moveBackward ) camera.translateZ( 10 );
   if ( moveLeft )     camera.translateX( - 10 );
   if ( moveRight )    camera.translateX( 10 );
   */
  lon += mouseX * 0.004;
  lat -= mouseY * 0.004;

  lat = Math.max(- 85, Math.min(85, lat));
  phi = ( 90 - lat ) * Math.PI / 180;
  theta = lon * Math.PI / 180;
  /*
   camera.target.position.x = 100 * Math.sin( phi ) * Math.cos( theta ) + camera.position.x;
   camera.target.position.y = 100 * Math.cos( phi ) + camera.position.y;
   camera.target.position.z = 100 * Math.sin( phi ) * Math.sin( theta ) + camera.position.z;
   */

  //pointLight.position.z = 800* Math.sin( 3*time );
  //pointLight.position.x = 800* Math.cos( 3*time );

  camera.position.x += ( mouseX - camera.position.x ) * .025;
  camera.position.y += ( - mouseY - camera.position.y ) * .025;

  // marching cubes

  if (effectController.resolution != resolution) {

    resolution = effectController.resolution;
    effect.init(resolution);

  }

  if (effectController.isolation != effect.isolation) {

    effect.isolation = effectController.isolation;

  }

  updateCubes(effect, time, effectController.numBlobs, effectController.floor, effectController.wallx, effectController.wallz);

  // materials

  if (effect.materials[ 0 ] instanceof THREE.MeshShaderMaterial) {

    if (current_material == "dotted2") {

      effect.materials[ 0 ].uniforms.uLineColor1.value.setHSV(effectController.hue, effectController.saturation, effectController.value);

    } else if (current_material == "hatching2") {

      u = effect.materials[ 0 ].uniforms;

      u.uLineColor1.value.setHSV(effectController.hue, effectController.saturation, effectController.value);
      u.uLineColor2.value.setHSV(effectController.hue, effectController.saturation, effectController.value);
      u.uLineColor3.value.setHSV(effectController.hue, effectController.saturation, effectController.value);
      u.uLineColor4.value.setHSV((effectController.hue + 0.2 % 1.0), effectController.saturation, effectController.value);

    } else {

      effect.materials[ 0 ].uniforms.uBaseColor.value.setHSV(effectController.hue, effectController.saturation, effectController.value);

    }

  } else {

    effect.materials[ 0 ].color.setHSV(effectController.hue, effectController.saturation, effectController.value);

  }

  // lights

  light.position.set(effectController.lx, effectController.ly, effectController.lz);
  light.position.normalize();

  pointLight.color.setHSV(effectController.lhue, effectController.lsaturation, effectController.lvalue);

  if (effectController.postprocessing) {

    materialFilm.uniforms.time.value += 0.01;

    renderer.render(scene, camera, rtTexture1);
    renderer.render(scenePost, cameraOrtho);

  } else {

    renderer.render(scene, camera);

  }

  stats.update();


}

// this controls content of marching cubes voxel field

function updateCubes(object, time, numblobs, floor, wallx, wallz) {

  object.reset();

  // fill the field with some metaballs

  var i, ballx, bally, ballz, subtract, strength;

  subtract = 12;
  strength = 1.2 / ( ( Math.sqrt(numblobs) - 1 ) / 4 + 1 );

  for (i = 0; i < numblobs; i++) {

    ballx = Math.sin(i + 1.26 * time * ( 1.03 + 0.5 * Math.cos(0.21 * i) )) * 0.27 + 0.5;
    bally = Math.abs(Math.cos(i + 1.12 * time * Math.cos(1.22 + 0.1424 * i))) * 0.77; // dip into the floor
    ballz = Math.cos(i + 1.32 * time * 0.1 * Math.sin(( 0.92 + 0.53 * i ))) * 0.27 + 0.5;

    object.addBall(ballx, bally, ballz, strength, subtract);

  }

  if (floor) object.addPlaneY(2, 12);
  if (wallz) object.addPlaneZ(2, 12);
  if (wallx) object.addPlaneX(2, 12);

}
;


function is_browser_compatible() {

  // WebGL support

  try {
    var test = new Float32Array(1);
  } catch(e) {
    return false;
  }

  return true;

}


</script>

</body>
</html>
